package hibatis.support.generator;

import hibatis.HibatisAutoConfig;
import hibatis.annotation.ExecuteSelect;
import hibatis.annotation.GeneratedSql;
import hibatis.ext.InterceptorContext;
import hibatis.support.Reflection;
import hibatis.support.StringUtils;
import hibatis.support.parse.EntityMeta;
import hibatis.support.parse.Table;
import hibatis.support.parse.ViewMeta;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created by huangdachao on 2018/6/14 16:59.
 */
public class SelectGenerator {
    private static final Logger LOG = LoggerFactory.getLogger(SelectGenerator.class);

    public static SqlSource generate(Configuration conf, Class<?> mapperClass, Method method) {
        EntityMeta em = EntityMeta.get(Reflection.getEntityClass(mapperClass));
        ExecuteSelect es = method.getAnnotation(ExecuteSelect.class);

        return parameterObject -> {
            Object[] args = Reflection.parseArgs(conf, method, parameterObject);
            String[] argNames = new ParamNameResolver(conf, method).getNames();

            InterceptorContext context = new InterceptorContext(mapperClass, em.getEntityClass(), method, args,
                    es == null ? GeneratedSql.Type.COUNT : GeneratedSql.Type.SELECT);
            HibatisAutoConfig.intercept(context);

            // from ... where ...
            FromAndWhereGenerator faw = new FromAndWhereGenerator(em, method, args, argNames, context);

            // select ... from ...
            StringBuilder sql = new StringBuilder("<script>select ");
            if (es == null) { // select count
                sql.append("count(*)");
            } else {
                sql.append(generateSelectColumnList(faw.getViewMeta(), faw.getTableAliasMap()));
            }
            sql.append(faw.generate());
            sql.append(faw.getPageAndSort().toSql(faw.getTableAliasMap()));
            sql.append("</script>");

            String sqlStr = sql.toString();
            LOG.debug(sqlStr);
            Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
            SqlSource sqlSource = conf.getLanguageRegistry().getDriver(XMLLanguageDriver.class)
                .createSqlSource(conf, sqlStr, parameterType);
            BoundSql bsql = sqlSource.getBoundSql(parameterObject);
            faw.getAdditionalParams().forEach(bsql::setAdditionalParameter);
            return bsql;
        };
    }

    public static String generateSelectColumnList(ViewMeta vm, Map<Table, String> tblAlias) {
        StringBuilder sql = new StringBuilder();
        if (vm.getColumns().size() == 0) {
            throw new RuntimeException("没有映射字段：" + vm.getEntityMeta().getEntityClass());
        }
        vm.getColumns().stream().sorted().forEach(tc -> {
            sql.append(tblAlias.get(tc.toTable())).append(".").append(tc.column);
            String columnAlias = StringUtils.snakeCase(vm.getField(tc));
            if (!columnAlias.equals(tc.column)) {
                sql.append(" ").append(columnAlias);
            }
            sql.append(",");
        });
        sql.setLength(sql.length() - 1);
        return sql.toString();
    }
}
